const { ccclass, property } = cc._decorator;
// 子游戏热更时，子游戏的存储路径会被添加到搜索路径中，为了避免污染搜索路径，预先保存干净的搜索路径
@ccclass
export default class TTHotUpdate extends cc.Component {
  _am: any = null;
  file_version: "version.manifest";
  file_project: "project.manifest";
  file_module: ""; //模块名称
  file_manifest: ""; //为空时，会读取路径文件，否则直接解析
  _version: "0.0.0"; //考虑到第二次发包，版本号可能不为1.0
  _hotupdataversion: "0.0.0"; //目标版本
  _times: number = 0; //更新失败重试次数（md5校验失败、解压失败、下载失败都会返回更新失败）
  _fullpath: String = "";

  addToQueue = null;
  changeQueue = null;
  queueStatus = false;  //是否在更新隊列中的標誌
  isupdating = false;  //正在更新中的標誌
  ischeckOver = false;  //更新检测完成的标志

  isupdataSucess = false //更新成功的標誌。包括已經是最新版本不需要更新
  needupdata = false; //需要更新的標誌
  isfail = false;  //更新失敗的標誌

  setQueueStatus(status: boolean) {
    this.queueStatus = status
  }
  getisupdataSucessStatus() {
    return this.isupdataSucess;
  }

  getneedupdataStatus() {
    return this.needupdata;
  }
  getisfailStatus() {
    return this.isfail;
  }
  getQueueStatus() {
    return this.queueStatus;
  }
  getisupdating() {
    return this.isupdating;
  }
  getischeckOver() {
    return this.ischeckOver;
  }
  //初始化相关参数
  init(data) {
    if (!data) return;
    this.file_version = data.file_version || "version.manifest";
    this.file_project = data.file_project || "project.manifest";
    this.file_module = data.file_module || "";
    this.file_manifest = data.file_manifest || "";
    this._version = data._version || "0.0.0";
  }

  checkCb(event) {
    console.log(
      event.getEventCode() + "  check cb=-------------------------------"
    );
    let error = false,
      over = false;
    switch (event.getEventCode()) {
      case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
        error = true;
        break;
      case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:
      case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST:
        error = true;
        break;
      case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:
        over = true;
        this.isupdataSucess = true;
        cc.vv.eventCustom.emit("ALREADY_UP_TO_DATE", {
          moduleTmp: this.file_module
        });
        break;
      case jsb.EventAssetsManager.NEW_VERSION_FOUND:
        console.log("NEW_VERSION_FOUND");
        this.needupdata = true;
        cc.vv.eventCustom.emit("NEW_VERSION_FOUND", {
          moduleTmp: this.file_module,
          version: this._hotupdataversion
        });
        break;
      default:
        break;
    }
    if (error || over) this._am.setEventCallback(null);
    if (error) {
      this.isfail = true
      cc.vv.eventCustom.emit("UPDATE_ERROR", { moduleTmp: this.file_module });
    }
    this.ischeckOver = true
  }

  startUpdate() {
    this._am.setEventCallback(this.updateCb.bind(this));
    this._am.update();
    this.isupdating = true;
  }
  updateCb(event) {
    let error = false,
      over = false;
    switch (event.getEventCode()) {
      case jsb.EventAssetsManager.ERROR_NO_LOCAL_MANIFEST:
        error = true;
        break;
      case jsb.EventAssetsManager.ERROR_DOWNLOAD_MANIFEST:
      case jsb.EventAssetsManager.ERROR_PARSE_MANIFEST:
        error = true;
        break;
      case jsb.EventAssetsManager.ALREADY_UP_TO_DATE:
        this.isupdataSucess = true;
        this.needupdata = false;
        over = true;
        cc.vv.eventCustom.emit("ALREADY_UP_TO_DATE", {
          moduleTmp: this.file_module
        });
        break;
      case jsb.EventAssetsManager.ERROR_DECOMPRESS:
        break;
      case jsb.EventAssetsManager.ERROR_UPDATING:
        break;
      case jsb.EventAssetsManager.UPDATE_FINISHED:
        console.log("upgrade finished =========================");
        over = true;
        this.needupdata = false;
        this.isupdataSucess = true;
        cc.vv.eventCustom.emit("UPDATE_FINISHED", {
          moduleTmp: this.file_module
        });
        break;
      case jsb.EventAssetsManager.UPDATE_FAILED:
        console.log("UPDATE_FAILED--------------------------------");
        this._times++;
        if (this._times < 3) {
          console.log("失败了,尝试重新下载", event.getMessage())
          this._am.downloadFailedAssets();
          return;
        }
        this._times = 0;
        over = true;
        cc.vv.eventCustom.emit("UPDATE_FAILED", {
          moduleTmp: this.file_module
        });
        break;
      case jsb.EventAssetsManager.UPDATE_PROGRESSION:
        cc.vv.eventCustom.emit("UPDATE_PROGRESSION", {
          event: event,
          moduleTmp: this.file_module
        });
        break;
      default:
        break;
    }
    if (error || over) {
      this._am.setEventCallback(null);
      this.isupdating = false;
    }
    if (error) {
      cc.vv.eventCustom.emit("UPDATE_ERROR", { moduleTmp: this.file_module, event: event });
    }
  }


  //upurl:热更地址   dir:更新路径

  check_update(upurl, dir) {
    //已经在排队中
    if (this.queueStatus) {
      return;
    }
    let path = jsb.fileUtils.getWritablePath() + cc.vv.GLConfig.resPath + "/" //dir; //资源存储路径
    this._fullpath = path;
    this.removeLatestTempManifest();
    this._am = new jsb.AssetsManager("", path + "$" + this.file_module);
    this._am.setVersionCompareHandle(
      function (versionA, versionB) {
        console.log(
          "JS Custom Version Compare: version A is " +
          versionA +
          ", version B is " +
          versionB
        );

        // cc.log(JSON.stringify(this._am.getLocalManifest().getSearchPaths()))
        this._hotupdataversion = versionB;
        let vA = versionA.split(".");
        let vB = versionB.split(".");
        for (let i = 0; i < vA.length; ++i) {
          let a = parseInt(vA[i]);
          let b = parseInt(vB[i] || 0);
          if (a === b) continue;
          else return a - b;
        }
        if (vB.length > vA.length) return -1;
        else return 0;
      }.bind(this)
    );
    if (cc.sys.os === cc.sys.OS_ANDROID) {
      this._am.setMaxConcurrentTask(1); //todo 并发下载最大任务数，此处需限制为依次下载子游戏 并发下载会减慢速度，有可能导致下载资源出错
    }
    this._am.setEventCallback(this.checkCb.bind(this));
    if (this._am.getState() === jsb.AssetsManager.State.UNINITED) {
      let url = path + this.file_project; //本地project.manifest文件路径
      var file_manifest;
      if (cc.loader.md5Pipe) {
        url = cc.loader.md5Pipe.transformURL(url);
      }
      var versiontemp = this._version
      var assetstemp = {}
      if (jsb.fileUtils.isFileExist(url)) {
        file_manifest = jsb.fileUtils.getStringFromFile(url);
        versiontemp = JSON.parse(file_manifest).version;
        assetstemp = JSON.parse(file_manifest).assets;
      }
      let fileurl = upurl + cc.vv.GLConfig.resPath + "/" + this.file_module + "/";
      let fileversion = fileurl + this.file_version;
      let fileproject = fileurl + this.file_project;
      let str = JSON.stringify({
        packageUrl: fileurl + versiontemp,
        remoteManifestUrl: fileproject,
        remoteVersionUrl: fileversion,
        version: versiontemp,
        assets: assetstemp,
        searchPaths: [],
        module: this.file_module
      });
      let manifest = new jsb.Manifest(
        this.file_manifest != "" ? this.file_manifest : str,
        path
      );
      this._am.loadLocalManifest(manifest, path + "$" + this.file_module);
    }

    if (
      !this._am.getLocalManifest() ||
      !this._am.getLocalManifest().isLoaded()
    ) {
      // this.emit('UPDATE_ERROR', { moduleTmp: this.file_module })
      cc.vv.eventCustom.emit("UPDATE_ERROR", { moduleTmp: this.file_module });
      return;
    }
    // this.emit('UPDATE_VERSION', { version: this._am.getLocalManifest().getVersion(), moduleTmp: this.file_module });
    cc.vv.eventCustom.emit("UPDATE_VERSION", {
      version: this._am.getLocalManifest().getVersion(),
      moduleTmp: this.file_module
    });
    // console.log('check update =-------------------------------------------')
    this._am.checkUpdate();
  }

  // 检测是否有残留的temp文件  删除掉
  removeLatestTempManifest() {
    var filename = this._fullpath + this.file_module + "_temp/project.manifest.temp";
    if (jsb.fileUtils.isFileExist(filename)) {
      jsb.fileUtils.removeFile(filename);
    }
  }

  get_manifest() {
    return this._am.getLocalManifest();
  }

  release() {
    cc.log("有hotupdata  this.queueStatus = ", this.queueStatus)
    this._am && this._am.setEventCallback(null);
  }
}
